<?php
/**
 * The question type class for the source code question type.
 *
 * @copyright &copy; 2012 Karol Danutama
 * @author karoldanutama@gmail.com
 * @license http://www.gnu.org/copyleft/gpl.html GNU Public License
 * @package LX
 *//** */

/**
 * The Source code question class
 *
 * TODO give an overview of how the class works here.
 */
class sourcecode_qtype extends default_questiontype {
	
	const SERVICE_ADDRESS = "http://localhost/lz/";
	const MAX_REGRADE_RETRY = 5;
	const SLEEP_TIME = 100;

    function name() {
        return 'sourcecode';
    }
    
    function is_manual_graded() {
		return true;
	}
    
    // TODO think about whether you need to override the is_manual_graded or
    // is_usable_by_random methods form the base class. Most the the time you
    // Won't need to.
    
    protected function deleteEvaluationSet($id) {
		$ch = curl_init();
		curl_setopt($ch, CURLOPT_URL, "http://localhost/lz/services/evaluationset/delete?clientid=1&clienttoken=1&id=" . $id);
		curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
		$output = curl_exec($ch);
		curl_close($ch);
	}
    
    protected function synchronizeEvaluationSet($id , $graderTypeId , $compilerTypeId , $filePath) {
		$ch = curl_init();
		if ($id == NULL)
			curl_setopt($ch, CURLOPT_URL, self::SERVICE_ADDRESS . "/services/evaluationset/add?clientid=1&clienttoken=1");
		else
			curl_setopt($ch, CURLOPT_URL, self::SERVICE_ADDRESS . "/services/evaluationset/update?clientid=1&clienttoken=1&id=" . $id);
		curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
		curl_setopt($ch, CURLOPT_POST, true);
		
		$data = array(
			'EvaluationSet[grader_type_id]' => $graderTypeId,
			'EvaluationSet[compiler_type_id]' => $compilerTypeId,
			'config[new]' => ''
		);
		
		if ($filePath != "") {
			$data['EvaluationSet[file]'] = "@" . $filePath;
		}
		
		curl_setopt($ch, CURLOPT_POSTFIELDS, $data);
		$output = curl_exec($ch);
		$info = curl_getinfo($ch);
		curl_close($ch);
		
		//echo $id . " " . $graderTypeId . " " . $compilerTypeId . " " . $filePath;
		//print_r($info);
		//echo $output;
		return json_decode($output , true);
	}

    /**
     * Save the units and the answers associated with this question.
     * @return boolean to indicate success of failure.
     */
    function save_question_options($question) {
		$result = true;
        $update = true;
        $answer = get_record("question_sourcecode", "question", $question->id);
        if (!$answer) {
            $answer = new stdClass;
            $answer->question = $question->id;
            $update = false;
        }
        $answer->compiletype   = $question->compiletype;
        $answer->gradingtype = $question->gradingtype;
        if ($_FILES['evaluatorfiles']['error'] == UPLOAD_ERR_OK) {
			$answer->evaluatorfiles = file_get_contents($_FILES['evaluatorfiles']['tmp_name']);
		}
		
		$result = $this->synchronizeEvaluationSet($answer->evaluationset_id , $answer->gradingtype , $answer->compiletype , $_FILES['evaluatorfiles']['tmp_name']);
        if ($result['success'] == 1 && isset($result['evaluationsetid'])) {
			$answer->evaluationset_id = $result['evaluationsetid'];
		}
		
        if ($update) {
            if (!update_record("question_sourcecode", $answer)) {
                $result = new stdClass;
                $result->error = "Could not update quiz answer!";
            }
        } else {
            if (!$answer->id = insert_record("question_sourcecode", $answer)) {
                $result = new stdClass;
                $result->error = "Could not insert quiz answer!";
            }
        }

        return $result;
    }

    protected function requestRegrade($requestid) {
		$ch = curl_init();
		curl_setopt($ch, CURLOPT_URL, self::SERVICE_ADDRESS . "services/grading/regrade?clientid=1&clienttoken=1&id=".$requestid);
		curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
		//curl_setopt($ch, CURLOPT_POST, true);
		
		//curl_setopt($ch, CURLOPT_POSTFIELDS, $data);
		$output = curl_exec($ch);
		$info = curl_getinfo($ch);
		curl_close($ch);
		return json_decode($output , true);
	}
    
    protected function requestGrade($evaluationsetid , $mode , $submitterid , $filePath , $fileName = "") {
		$ch = curl_init();
		curl_setopt($ch, CURLOPT_URL, self::SERVICE_ADDRESS . "services/grading/grade?clientid=1&clienttoken=1");
		curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
		curl_setopt($ch, CURLOPT_POST, true);
		
		$data = array(
			'GradeRequest[evaluationset_id]' => $evaluationsetid,
			'GradeRequest[mode]' => $mode, //TODO: fix this
			'GradeRequest[submitter_id]' => $submitterid,
			'GradeRequest[file]' => "@" . $filePath,
			'GradeRequest[source_file]' => $fileName
		);
		
		curl_setopt($ch, CURLOPT_POSTFIELDS, $data);
		$output = curl_exec($ch);
		$info = curl_getinfo($ch);
		curl_close($ch);
		
		//echo $id . " " . $graderTypeId . " " . $compilerTypeId . " " . $filePath;
		//print_r($info);
		//echo $output;
		return json_decode($output , true);
	}
    
    function save_session_and_responses(&$question, &$state) {
        // TODO package up the students response from the $state->responses
        $responses = '';
        $questionRecord = get_record("question_sourcecode", "question", $state->question);
        
        
        if (isset($_FILES['solutionfile'])) {
			if ($_FILES['solutionfile']['error'][$state->question] != UPLOAD_ERR_OK) {
				$files = array();
				foreach($_POST['doppel'] as $key => $elm) {
					if (isset($_POST['selected'][$key]))
						$files[$key] = json_decode(urldecode($elm) , true);
				}
				
				$path = "";
				$name = "";
				if (count($files) > 1) {
				$archive = new ZipArchive();
				$path = "/tmp/" . $state->question . "-" . $state->attempt . ".zip";
				$name = $path;
				$archive->open($path , ZipArchive::CREATE);
				foreach ($files as $key => $file) {
					$archive->addFromString($file['fileName'] , $file['content']);
				}
				$archive->close();
				}
				else {
					foreach ($files as $key => $file) {
						//print_r($file);
						$path = "/tmp/" . $file['fileName'];
						file_put_contents($path , $file['content']);
						$name = $file['fileName'];
					}
				}
				//exit;
				
				$result = $this->requestGrade($questionRecord->evaluationset_id , 0 , "Test Moodle" , $path , $name);
				
				$record = new stdClass;
				$record->file = addslashes(file_get_contents($path));
				//echo file_get_contents($path);
				$record->mime = "zip";
				$record->name = $path;
				$record->question = $state->question;
				$record->attempt = $state->attempt;
				
				if ($result['success'] == 1 && isset($result["request_id"])) {
					$record->request_id = $result['request_id'];
				}
				
				insert_record("question_sourcecode_files", $record);
				unlink($path);
			}
			else {
				$result = $this->requestGrade($questionRecord->evaluationset_id , 0 , "Test Moodle" , $_FILES['solutionfile']['tmp_name'][$state->question] , $_FILES['solutionfile']['name'][$state->question]);
				
				$record = new stdClass;
				$record->file = addslashes(file_get_contents($_FILES['solutionfile']['tmp_name'][$state->question]));
				$record->mime = $_FILES['solutionfile']['type'][$state->question];
				$record->name = $_FILES['solutionfile']['name'][$state->question];
				$record->question = $state->question;
				$record->attempt = $state->attempt;
				
				if ($result['success'] == 1 && isset($result["request_id"])) {
					$record->request_id = $result['request_id'];
				}
				
				insert_record("question_sourcecode_files", $record);
			}
		}
		
		$state->responses[''] = $responses;
		
		/*if (!set_field('question_states', 'answer', $responses, 'id',$state->id)) {
            return false;
        }*/
        
        return true;
    }
    
    protected function requestGradingResult($requestid) {
		$ch = curl_init();
		curl_setopt($ch, CURLOPT_URL, self::SERVICE_ADDRESS . "services/grading/detail?clientid=1&clienttoken=1&id=" . $requestid);
		curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
		
		$output = curl_exec($ch);
		$info = curl_getinfo($ch);
		curl_close($ch);
		
		//echo $id . " " . $graderTypeId . " " . $compilerTypeId . " " . $filePath;
		//print_r($info);
		//echo $output;
		return json_decode($output , true);
	}
	
	protected function getSubmittedFiles($state , $fields = "id , request_id , name , timestamp") {
		$retval = null;
		$retval = get_records_select('question_sourcecode_files' , "question = " . $state->question . " AND attempt = " . $state->attempt , "timestamp DESC" , $fields);
		return $retval;
	}
    
    function print_question_formulation_and_controls(&$question, &$state, $cmoptions, $options) {
        global $CFG;

		$submittedFiles = $this->getSubmittedFiles($state);

        $readonly = empty($options->readonly) ? '' : 'disabled="disabled"';

		$questionRecord = get_record("question_sourcecode", "question", $state->question);
		$evaluationset_id = $questionRecord->evaluationset_id;
		
        // Print formulation
        $questiontext = $this->format_text($question->questiontext,
                $question->questiontextformat, $cmoptions);
        $image = get_question_image($question, $cmoptions->course);
		
    
        // TODO prepare any other data necessary. For instance
        $feedback = '';
        if ($options->feedback) {
			
        }
        $gradingResult = NULL;
		if (!empty($options->feedback) || $cmoptions->timelimit == 0) {
			/*$sourceFileRecord = get_record("question_sourcecode_files", "state_id", $state->id);
			
			if ($sourceFileRecord) {
				$gradingResult = ($this->requestGradingResult($sourceFileRecord->request_id));
			}*/
			
			if ($submittedFiles != null) {
				foreach ($submittedFiles as $key => $f) {
					$gradingResult = $this->requestGradingResult($f->request_id);
					$submittedFiles[$key]->gradingResult = $gradingResult;
				}
			}
		}
        
        include("$CFG->dirroot/question/type/sourcecode/display.html");
    }
    
    function grade_responses(&$question, &$state, $cmoptions) {
		//$state->responses[''] = clean_param($state->responses[''], PARAM_CLEAN);

        $state->raw_grade = 0;
        $state->penalty = 0;
        $state->event = QUESTION_EVENTSUBMIT;
        
        if ($_GET['mode'] == "regrade") { ///Regrade, achieve grading result from LX
			$submittedFiles = $this->getSubmittedFiles($state);
			foreach ($submittedFiles as $f) {
				$regradeResponse = $this->requestRegrade($f->request_id);
				
				/*$rep = 0;
				do {
					$status = $gradingResult['detail']['status'];
					$rep++;
					if ($status == 0 || $status == 1) {
						usleep(self::SLEEP_TIME);
					}
				} while (($status == 0 || $status == 1) && $rep <= self::MAX_REGRADE_RETRY);*/
				
				/*$gradingResult = $this->requestGradingResult($f->request_id);
				if (isset($gradingResult['detail']['report']['grade_result']['score'])) {
					$state->raw_grade = $gradingResult['detail']['report']['grade_result']['score'];
				}*/
			}
			//$state->event = QUESTION_EVENTGRADE;
		}

        return true;
    }
    
    
	function compare_responses(&$question, $state, $teststate) {
        // TODO write the code to return two different student responses, and
        // return two if the should be considered the same.
        return parent::compare_responses($question , $state , $teststate);
    }

    /**
     * Checks whether a response matches a given answer, taking the tolerance
     * and units into account. Returns a true for if a response matches the
     * answer, false if it doesn't.
     */
    function test_response(&$question, &$state, $answer) {
        // TODO if your code uses the question_answer table, write a method to
        // determine whether the student's response in $state matches the    
        // answer in $answer.
        //return false;
        return parent::test_response($question , $state , $answer);
    }

    function check_response(&$question, &$state){
        // TODO
        //return false;
        return parent::check_response($question , $state);
    }

    function get_correct_responses(&$question, &$state) {
        // TODO
        //return false;
        return parent::get_correct_responses($question , $state);
    }

    function get_all_responses(&$question, &$state) {
        $result = new stdClass;
        // TODO
        //return $result;
        return parent::get_all_responses($question , $state);
    }

    function get_actual_response($question, $state) {
        // TODO
        $responses = '';
        //return $responses;
        return parent::get_actual_response($question , $state);
    }

    /**
     * Backup the data in the question
     *
     * This is used in question/backuplib.php
     */
    function backup($bf,$preferences,$question,$level=6) {
        $status = true;

        // TODO write code to backup an instance of your question type.

        return $status;
    }

    /**
     * Restores the data in the question
     *
     * This is used in question/restorelib.php
     */
    function restore($old_question_id,$new_question_id,$info,$restore) {
        $status = true;

        // TODO write code to restore an instance of your question type.

        return $status;
    }
    
    /**
     * Deletes question from the question-type specific tables
     *
     * @param integer $questionid The question being deleted
     * @return boolean to indicate success of failure.
     */
    function delete_question($questionid) {
        // TODO delete any
        return parent::delete_question($questionid);
    }

    function create_session_and_responses(&$question, &$state, $cmoptions, $attempt) {
        // TODO create a blank repsonse in the $state->responses array, which    
        // represents the situation before the student has made a response.
        return parent::create_session_and_responses($question, $state, $cmoptions, $attempt);
    }

    function restore_session_and_responses(&$question, &$state) {
        // TODO unpack $state->responses[''], which has just been loaded from the
        // database field question_states.answer into the $state->responses array.
        return parent::restore_session_and_responses($question , $state);
    }
    /**
     * @return boolean to indicate success of failure.
     */
    function get_question_options($question) {
		return true;
    }

}

// Register this question type with the system.
question_register_questiontype(new sourcecode_qtype());
?>
